from PyQt6 import QtWidgets
from PyQt6.QtWidgets import QFileDialog, QDialog, QInputDialog
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QTextCursor
from TrosParser.tros_parser import Ui_Dialog
import time
import os
import shutil
import subprocess
import json
import TrosParser.libs.dbcs_parser as dbcs_parser
import TrosParser.libs.log as log
import TrosParser.libs.gen_proto as gen_proto
import TrosParser.libs.gen_canjson as gen_canjson
import TrosParser.libs.gen_struct as gen_struct

class TrosParserDialog(QDialog, Ui_Dialog):
    def __init__(self):
        super(TrosParserDialog, self).__init__()
        self.setupUi(self)
        self.pushButton.clicked.connect(self.open_dbc_file)
        self.pushButton_2.clicked.connect(self.select_proto_path)  # 选择 proto 路径
        self.pushButton_3.clicked.connect(self.select_struct_path)  # 选择 struct 路径
        self.output_buffer = []
        self.textEdit.setPlainText('')
        self.proto_path = ""
        self.struct_path = ""
        self.output_dir = 'output'  # 定义输出目录
        self.textEdit_2.setPlainText('')

    def select_proto_path(self):
        self.proto_path = ""
        self.struct_path = ""
        self.proto_path = QFileDialog.getExistingDirectory(self, "Select Proto Directory")
        if self.proto_path:
            self.log_output_2(f'Selected proto path: {self.proto_path}')
        self.check_and_run_struct2proto()

    def select_struct_path(self):
        self.struct_path = QFileDialog.getExistingDirectory(self, "Select Struct Directory")
        if self.struct_path:
            self.log_output_2(f'Selected struct path: {self.struct_path}')
        self.check_and_run_struct2proto()

    def check_and_run_struct2proto(self):
        # 当 proto 和 struct 路径都选择完毕时，弹出输入框让用户输入生成的 cpp 文件名
        if self.proto_path and self.struct_path:
            output_cpp_file, ok = QInputDialog.getText(self, 'Input Dialog',
                                                       'Enter output cpp file name (with .cpp extension):')
            if ok and output_cpp_file:
                # 确保文件名以 .cpp 结尾
                if not output_cpp_file.endswith('.cpp'):
                    output_cpp_file += '.cpp'

                full_output_path = os.path.join(self.output_dir, output_cpp_file)  # 在 output 文件夹下生成文件
                self.log_output_2(f'Generating {full_output_path}...')
                try:
                    self.call_struct2proto(self.proto_path, self.struct_path, full_output_path)
                    self.log_output_2('struct2proto.py script executed successfully.')
                except Exception as e:
                    self.log_output_2(f'Error during struct2proto execution: {str(e)}')

    def call_struct2proto(self, proto_path, struct_path, output_file):
        # 构造并调用 struct2proto.py 命令
        command = ['python', 'TrosParser/struct2proto.py', proto_path, struct_path, output_file]
        result = subprocess.run(command, check=True, capture_output=True, text=True)
        self.log_output_2(result.stdout)
        if result.stderr:
            self.log_output_2(result.stderr)

    def open_dbc_file(self):
        file_name, _ = QFileDialog.getOpenFileName(self, "Select DBC File", "", "DBC Files (*.dbc);;All Files (*)")
        if file_name:
            self.textEdit.setText(file_name)

            # 修改 config.json 中的 file_name
            self.update_config_file(file_name)

            # 运行 dbc 转换功能
            self.run_dbc_conversion()

    def update_config_file(self, dbc_path):
        config_path = 'TrosParser/config.json'
        try:
            with open(config_path, 'r', encoding='utf-8') as f:
                config_data = json.load(f)

            # 更新 file_name，不包括 .dbc 后缀
            if 'can_node_list' in config_data and len(config_data['can_node_list']) > 0:
                config_data['can_node_list'][0]['file_name'] = os.path.splitext(os.path.basename(dbc_path))[0]  # 去掉 .dbc 后缀

            # 将更改写回 config.json
            with open(config_path, 'w', encoding='utf-8') as f:
                json.dump(config_data, f, ensure_ascii=False, indent=4)

            self.log_output(f'Updated config.json with file_name: {os.path.basename(dbc_path)} (without .dbc extension)')

            # 拷贝 dbc 文件到 TrosParser/input 目录
            self.copy_dbc_file(dbc_path)

        except Exception as e:
            self.log_output(f'Error updating config.json: {str(e)}')

    def copy_dbc_file(self, dbc_path):
        input_dir = 'TrosParser/input'
        os.makedirs(input_dir, exist_ok=True)
        try:
            target_path = os.path.join(input_dir, os.path.basename(dbc_path))

            # 检查源文件和目标文件是否相同
            if os.path.abspath(dbc_path) != os.path.abspath(target_path):
                shutil.copy(dbc_path, target_path)
                self.log_output(f'Copied DBC file to {input_dir}.')
            else:
                self.log_output('DBC file is already in the input directory. No need to copy.')

        except Exception as e:
            self.log_output(f'Error copying DBC file: {str(e)}')

    def log_output(self, message):
        self.output_buffer.append(message)
        self.textEdit.setPlainText('\n'.join(self.output_buffer))

        # 将光标移动到文本末尾
        cursor = self.textEdit.textCursor()
        cursor.movePosition(QTextCursor.MoveOperation.End)
        self.textEdit.setTextCursor(cursor)
        self.textEdit.ensureCursorVisible()

    def log_output_2(self, message):
        self.output_buffer.append(message)
        self.textEdit_2.setPlainText('\n'.join(self.output_buffer))

        # 将光标移动到文本末尾
        cursor = self.textEdit_2.textCursor()
        cursor.movePosition(QTextCursor.MoveOperation.End)
        self.textEdit_2.setTextCursor(cursor)
        self.textEdit_2.ensureCursorVisible()

    def run_dbc_conversion(self):
        self.log_output('-----------------------------------------------')
        self.log_output('start generating proto file, json file and struct file')
        timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
        self.log_output('Started: {}'.format(timestamp))
        self.log_output('-----------------------------------------------')

        # 清理输出目录
        output_dir = 'output'
        if os.path.exists(output_dir):
            shutil.rmtree(output_dir)
        os.makedirs(output_dir)

        logging = log.setup_logger('log', os.path.join(output_dir, 'log.txt'))
        dp = dbcs_parser.dbcs_parser()

        # 直接传入 None
        dp.parse_dbcs("TrosParser/input", logging)

        # 生成 proto 文件
        try:
            gen_proto.gen(dp, logging)
            self.log_output('-----------------------------------------------')
            self.log_output('proto generation DONE')
            timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
            self.log_output('Ended: {}'.format(timestamp))
            self.log_output('-----------------------------------------------\n')
        except Exception as e:
            self.log_output(f'Error during proto generation: {str(e)}')
            return

        # 生成 json 文件
        try:
            gen_canjson.gen_can_json(dp, logging)
            self.log_output('-----------------------------------------------')
            self.log_output('json generation DONE')
            timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
            self.log_output('Ended: {}'.format(timestamp))
            self.log_output('-----------------------------------------------')
        except Exception as e:
            self.log_output(f'Error during JSON generation: {str(e)}')
            return

        # 生成 struct 文件
        try:
            gen_struct.gen_struct(dp, logging)
            self.log_output('-----------------------------------------------')
            self.log_output('struct generation DONE')
            timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time()))
            self.log_output('Ended: {}'.format(timestamp))
            self.log_output('-----------------------------------------------')
        except Exception as e:
            self.log_output(f'Error during struct generation: {str(e)}')
            return

        # 调用 canparser.py 生成 DA_can_parser.h
        self.call_script('TrosParser/canparser.py', output_dir)

        # 调用 struct2proto.py 生成 can2proto.cpp 和 can2proto.h
        proto_file = os.path.join(output_dir, 'DA.proto')
        header_file = os.path.join(output_dir, 'can_node_DA.h')
        output_cpp_file = os.path.join(output_dir, 'can2proto.cpp')
        self.call_script('TrosParser/struct2proto.py', output_dir, proto_file, header_file, output_cpp_file, 'with_hdr')

    def call_script(self, script_name, output_dir, *args):
        try:
            # 构造命令和参数
            command = ['python', script_name] + list(args)
            result = subprocess.run(command, cwd='.', check=True, capture_output=True, text=True)
            self.log_output(f'Called {script_name} successfully.')
            self.log_output(result.stdout)  # 打印脚本的标准输出
        except subprocess.CalledProcessError as e:
            self.log_output(f'Error calling {script_name}: {e.stderr}')  # 打印错误信息
